#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os.path
import platform
import pwd
import psutil

import pexpect

from common.cleaner import clear
from common.common import execute_cmd, execute_cmd_oversize_return_value, check_command_injection
from common.logger import Logger
from common.util import configparser_utils
from db2.comm.constant import DEPLOY_OPERATING_SYSTEMS, DEPLOY_PLAT
from db2.comm.const import Db2Const, Db2ConfigFileName, Db2ConfigSection
from db2.comm.util.common_util import Db2CommonUtil

LOGGER = Logger().get_logger(filename="db2.log")


def get_cmd_result(db2_user_name, db2_cmd, encoding=""):
    Db2CommonUtil.check_os_user_with_ex(db2_user_name)
    ex_cmd = f"su - {db2_user_name} -c '{db2_cmd}'"
    if encoding == "":
        encoding = get_lang_value(db2_user_name)
    LOGGER.info(f"cmd encoding: {encoding}")
    return_code, out_info, err_info = execute_cmd(ex_cmd, encoding=encoding)
    ret = (return_code == "0")
    ret_const = out_info if ret else err_info
    return ret, ret_const


def get_db2_databases(db2_user_name):
    Db2CommonUtil.check_os_user_with_ex(db2_user_name)
    encoding = get_lang_value(db2_user_name)
    var_cmd = f"db2 list db directory"
    ret, output = get_cmd_result(db2_user_name, var_cmd, encoding)
    return ret, output


def get_db2_instance(db2_user_name):
    var_cmd = f"db2 get instance"
    ret, cont = get_cmd_result(db2_user_name, var_cmd)
    return ret, cont


def get_operating_system(db2_user_name):
    bits = platform.architecture()
    name = platform.system()
    if name not in DEPLOY_PLAT:
        LOGGER.error("Get deploy operating platform failed.")
        return False, ""
    LOGGER.info(f"Get deploy operating system: {name}")
    if name == 'Linux':
        var_cmd = "cat /etc/*release"
        ret, db2_system = get_cmd_result(db2_user_name, var_cmd)
        if not ret:
            LOGGER.error("Get os system failed !")
            return False, ""
        for system_name in DEPLOY_OPERATING_SYSTEMS:
            if system_name in db2_system:
                LOGGER.info(f"Get deploy operating system: {system_name}")
                return True, (system_name, bits)
    else:
        system_name = name
        return True, (name, bits)
    LOGGER.error("Get deploy operating system failed.")
    return False, ""


def connect_to_database_nodes(db2_user_name, db2_database_name):
    Db2CommonUtil.check_injection(db2_database_name)
    var_cmd = f"db2 connect to {db2_database_name}; db2 list nodes; db2 terminate"
    ret, cont = get_cmd_result(db2_user_name, var_cmd)
    return ret, cont


def check_cluster_node(db2_user_name):
    var_cmd = f"cat ~/sqllib/db2nodes.cfg"
    ret, cont = get_cmd_result(db2_user_name, var_cmd)
    return ret, cont


def check_cluster_node_right():
    var_cmd = f"cat /etc/hosts"
    ret, cont, err = execute_cmd(var_cmd)
    ret_const = cont if ret else err
    return ret, ret_const


def check_user_password(db2_user_name, db2_user_password):
    Db2CommonUtil.check_os_user_with_ex(db2_user_name)
    cmd = f"su - {db2_user_name} -c 'su - {db2_user_name}'"
    LOGGER.info(f"cmd:{cmd}")
    lang_value = get_lang_value(db2_user_name)
    child = pexpect.spawn(cmd, timeout=Db2Const.TIMEOUT, maxread=Db2Const.MAXREAD, encoding=lang_value)
    # 登录用户
    config_path = os.path.join(os.path.dirname(__file__), Db2ConfigFileName.file_name)
    LOGGER.info(f"config_path:{config_path}")
    config_dict = configparser_utils.read_config_section(config_path, Db2ConfigSection.SECTION_PEXPECT)
    expect_cmd = list(config_dict.values())
    expect_cmd.extend([pexpect.TIMEOUT, pexpect.EOF])
    user_index = child.expect(expect_cmd)
    if user_index not in (0, 1, 2):
        LOGGER.info("Incorrect username!")
        child.close()
        return False, user_index
    child.sendline(db2_user_password)
    clear(db2_user_password)
    pwd_index = child.expect(["-bash", "\$", "no job control in this shell", pexpect.TIMEOUT, pexpect.EOF])
    LOGGER.info(f"pwd_index:{pwd_index}")
    if pwd_index in (0, 1, 2):
        child.close()
        return True, pwd_index
    child.close()
    return False, pwd_index


def get_lang_value(db2_user_name):
    Db2CommonUtil.check_os_user_with_ex(db2_user_name)
    ex_cmd = f"su - {db2_user_name} -c 'echo $LANG'"
    return_code, out_info, err_info = execute_cmd(ex_cmd)
    ret = (return_code == "0")
    lang_value = out_info if ret else err_info
    encoding = "utf-8"
    if not ret or not lang_value:
        LOGGER.error(f"Get user encoding fail, ret: {ret}, lang_value: {lang_value} use default encoding utf-8.")
        return encoding
    lang_value = lang_value.strip('\n').strip()
    LOGGER.info(f"lang_value:{lang_value}")
    if check_command_injection(lang_value):
        LOGGER.error(f"Check locale fail, out: {lang_value} use default encoding utf-8.")
        return encoding
    user_locale = lang_value.split(".")
    if len(user_locale) != 2:
        LOGGER.error("The locale format is incorrect use default encoding utf-8.")
        return encoding
    return user_locale[-1]


def check_os_user(os_user):
    """
    根据系统用户名获取用户ID和用户组ID
    """
    try:
        user_info = pwd.getpwnam(os_user)
    except KeyError as ex:
        LOGGER.error(f"Current system does not have user: {os_user}.")
        return False
    return True


def check_ha_dr_cluster_node(db2_user_name, db2_database_name):
    Db2CommonUtil.check_os_user_with_ex(db2_user_name)
    Db2CommonUtil.check_injection(db2_database_name)
    var_cmd = f"su - {db2_user_name} -c 'db2pd -db {db2_database_name} -hadr'"
    ret, cont, _ = execute_cmd_oversize_return_value(var_cmd)
    return ret, cont


def check_ha_dr_cluster_cfg(db2_user_name, db2_database_name):
    Db2CommonUtil.check_os_user_with_ex(db2_user_name)
    Db2CommonUtil.check_injection(db2_database_name)
    var_cmd = f"su - {db2_user_name} -c 'db2 get db cfg for {db2_database_name}'"
    ret, cont, _ = execute_cmd_oversize_return_value(var_cmd)
    return ret, cont


def check_process_service(db2_username):
    LOGGER.info(f"Begin to check process service")
    pid_list = psutil.pids()
    for os_pid in pid_list:
        try:
            pid_name = psutil.Process(os_pid).username()
        except Exception:
            LOGGER.debug(f"This is interim process,pid is {os_pid} ")
            continue
        try:
            tmp_cmd = psutil.Process(os_pid).cmdline()
        except Exception:
            LOGGER.debug(f"This is interim process,pid is {os_pid} ")
            continue
        if not tmp_cmd:
            continue
        if pid_name == db2_username and "db2sysc" in tmp_cmd[0]:
            return True
    return False
